#pragma once

#include <iostream>
#include <memory>
#include <string>
#include <jsoncpp/json/json.h>

static const std::string sep = "\r\n";

// 设计一个网络计算器，对传输的数据做如下约定：
//   1. 两个操作数，x是前操作数，y是后操作数
//   2. 两个操作数都是整数，结果也是整数
//   3. code = 0, 计算成功      code = 1, 发生除零操作      code = 2, 非法操作

// 需要设计一下协议的报头 和 报文的完整格式，
// 由于字节流并不能自己确定一个完整字符串是什么
// 所以需要规定一个完整的报文流必须是形如: "len"\r\n"{json}"\r\n
// 其中len 是有效载荷的长度(计算长度时不计算 \r 和 \n 这两种字符)
// 这样就保证了只有在读取到 len 这么长 的json字符之后才会进行发送
// 添加报头
std::string Encode(const std::string &jsonstr)
{
    int len = jsonstr.size();
    std::string lenstr = std::to_string(len);
    return lenstr + sep + jsonstr + sep;
}

// 这里的形参不能带const，这是因为在头部找到一个完整字符串之后，应该将这个完整字符串删除
// 如果有了Encode, 将会实现字节流只会是
// "len"\r\n"{json}"\r\n 这一字符串的一部分为结尾的一整个字符串
// 也就是说 字节流 不会从 "len"\r\n"{json}"\r\n 这个字符串中间去掉几个字符后作为子串
std::string Decode(std::string &packagestream)
{
    auto pos = packagestream.find(sep);
    if (pos == std::string::npos)
    {
        // 这种情况中 len 都没有读完， 或者只读到了"len"\r
        // 此时返回空字符串
        return std::string();
    }
    std::string lenstr = packagestream.substr(0, pos);
    int len = std::stoi(lenstr);
    // total 就是一个完整的报文的长度
    int total = lenstr.size() + len + 2 * sep.size();
    if (packagestream.size() < total)
    {
        // 这种情况中 没有找到一个完整的字符串
        // 此时返回空字符串
        return std::string();
    }

    // 走到这，已经具备了提取一个完整 json 串的条件了
    std::string jsonstr = packagestream.substr(pos + sep.size(), len);
    // basic_string 中有以下用法，如果之传入一个参数，将把这个参数赋值给下面的pos
    //basic_string& erase (size_type pos = 0, size_type len = npos);
    packagestream.erase(0, total);
    return jsonstr;
}

class Request
{
public:
    Request()
    {}
    Request(int x, int y, char oper)
        :_x(x)
        ,_y(y)
        ,_oper(oper)
    {

    }

    bool Serialize(std::string *out)
    {
        // 1. 使用现成的库: xml, json(jsoncpp), protobuf
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;
        // FastWriter  和  StyledWriter只有在字符串中是否添加换行符的区别
        Json::FastWriter writer;
        // Json::StyledWriter writer;
        std::string s = writer.write(root);
        *out = s;
        return true;
    }

    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _oper = root["oper"].asInt();

        return true;
    }

    void PrintforRequest()
    {
        std::cout << _x << std::endl;
        std::cout << _y << std::endl;
        std::cout << _oper << std::endl;
    }

    int getx()
    {
        return _x;
    }
    
    int gety()
    {
        return _y;
    }

    char getoper()
    {
        return _oper;
    }

    void setVal(int x, int y, char oper)
    {
        _x = x;
        _y = y;
        _oper = oper;
    }

    ~Request()
    {

    }
private:
    int _x;
    int _y;
    // + - * / % 
    // x oper y
    char _oper; 
};

class Response
{
public:
    Response()
        :_result(0)
        ,_code(0)
        ,_desc("success")
    {

    }

    bool Serialize(std::string *out)
    {
        // 1. 使用现成的库: xml, json(jsoncpp), protobuf
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;
        root["desc"] = _desc;
        // FastWriter  和  StyledWriter只有在字符串中是否添加换行符的区别
        Json::FastWriter writer;
        // Json::StyledWriter writer;
        std::string s = writer.write(root);
        *out = s;
        return true;
    }

    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if(!res) return false;
        _result = root["result"].asInt();
        _code = root["code"].asInt();
        _desc = root["desc"].asString();

        return true;
    }

    void PrintforResponse()
    {
        std::cout << "result: " << _result << ", code: " << _code << ", desc: " << _desc << std::endl;
    }

    ~Response()
    {
        
    }
// private:
    int _result;
    int _code;
    std::string _desc;
};

class Factory
{
public:
    static std::shared_ptr<Request> buildRequest()
    {
        return std::make_shared<Request>();
    }

    static std::shared_ptr<Response> buildResponse()
    {
        return std::make_shared<Response>();
    }

};
